using System;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using System.Xml.Xsl;
using System.Net.Mail;

/*	-----------------------------------------------------------------------	
	Copyright:	umlungu consulting (Pty) ltd
	Author:		Alan Benington	
	Started:	2005-05-15	
	Status:		release	
	Version:	2.0.0
	Buidl:		20070611
	License:	GNU General Public License
	-----------------------------------------------------------------------	*/
	
/*	---------------------------------------------------------------------------	
	Development Notes:
	==================
	20070524:	Modification for .Net 2.0
	20070611:	Fixed bug in destructor
	---------------------------------------------------------------------------	*/
	
namespace umlungu.engineX.blogX {
	/// <summary>
	/// umlungu engineX blog user class.
	/// </summary>
	public class x_user {
		#region Invisible properties
		private const double _expiry = 20.0;		// minutes to expire token
		private string _path;
		#endregion

		#region Visible properties
		private XmlDocument _users;
		/// <summary>Definiton of the property</summary>
		/// <value>Description of the value of the property</value>
		public XmlDocument UserDoc {	get	{	return _users;	}	}
		/// <summary>Definiton of the property</summary>
		/// <value>Description of the value of the property</value>
		public XmlNode Users {	get	{	return _users.DocumentElement;	}	}
		private XmlDocument _root;
		/// <summary>Definiton of the property</summary>
		/// <value>Description of the value of the property</value>
		public XmlNode Root	{	get	{	return _root.DocumentElement;	}	}
		private XmlElement user;
		/// <summary>A particular user (identified by the GetUser method)</summary>
		/// <value>An XmlElement containing the user definition</value>
		public XmlElement User	{
			get	{	return user;	}	
			set	{	user = value;	}
		}
		private string token;
		/// <summary>Definiton of the property</summary>
		/// <value>Description of the value of the property</value>
		public string Token	{
			get	{	return token;	}	
			set	{	token = value;	}
		}
		/// <summary>Definiton of the property</summary>
		/// <value>Description of the value of the property</value>
		public string Username	{	get	{	return user.GetAttribute("name");	}	}
		/// <summary>Definiton of the property</summary>
		/// <value>Description of the value of the property</value>
		public string Name	{	get	{	return (user.SelectSingleNode("firstname").InnerText + " " + user.SelectSingleNode("surname").InnerText);	}	}
		/// <summary>Definiton of the property</summary>
		/// <value>Description of the value of the property</value>
		public string Email	{	get	{	return (user.SelectSingleNode("email").InnerText);	}	}
		/// <summary>Global Unique Identifier</summary>
		/// <value>A string containing a guid</value>
		public string Guid {
			get { return getToken(); }
		}
		/// <summary>The user rights</summary>
		/// <value>A XmlElement containing the user rights list</value>
		public XmlElement Rights {
			get { return _root.SelectSingleNode("//groups/group[@name='" + user.GetAttribute("group") + "']/rights") as XmlElement; }
		}
		#endregion

		#region Constructors/Destructors
		/// <overloads>Constructor</overloads>
		/// <summary>Default constructor - used to access static properties and methods</summary>
		public x_user() {
		}
		/// <summary>Constructor</summary>
		/// <param name="name">The web application object</param>
		/// <param name="application">The web application object</param>
		public x_user(HttpApplicationState application, string name) {
			application.Lock();
			_users = (XmlDocument) application["UserX"];
			_root = (XmlDocument) application["BlogX"];
			_path = (string) application["DataPathX"];
			getUser(name);
			application.UnLock();
		}
		/// <summary>Constructor</summary>
		/// <param name="application">The web application object</param>
		public x_user(HttpApplicationState application) {
			application.Lock();
			_users = (XmlDocument) application["UserX"];
			_root = (XmlDocument) application["BlogX"];
			_path = (string) application["DataPathX"];
			application.UnLock();
		}
		/// <summary>Destructor</summary>
		~x_user() {
			if (user != null && _users != null) {
				if (_users.Equals(user.ParentNode))
					_users.RemoveChild(user);
			}
		}
		#endregion
		
		#region Public methods
		/// <overloads>Validates the right of the user.</overloads>
		/// <summary>checks token, right and user</summary>
		public void Validate(string token, string right, string name) {
			if (!checkPublic(right)) {
				validate(token);
				try {
					checkUser(token, name);
				} catch(x_exception e) {
					string msg = e.Message;
					checkRight(right);
				}
			}
		}
		/// <summary>checks token and right</summary>
		public void Validate(string token, string right) {
			if (!checkPublic(right)) {
				validate(token);
				checkRight(right);
			}
		}
		/// <summary>checks token</summary>
		public void Validate(string token) {
			validate(token);
		}
		private void validate(string token) {
			scan(token);
			if (token == "" || token == "null")
				throw(new x_exception("error_expired", "token expired"));
			XmlElement userel = (XmlElement) _users.SelectSingleNode("//users/user[@token='"+token+"']");
			if (userel == null)
				throw(new x_exception("error_notoken", "user is not logged on"));
			user = (XmlElement) userel.CloneNode(true);
			checkToken();
			zapPassword();
		}

		/// <summary>Logs user in</summary>
		public void Login(string name, string password) {
			getUser(name);
			if (user.GetAttribute("password") != password)
				throw(new x_exception("error_password", "incorrect password"));
			setToken();
			zapPassword();
		}
		
		/// <summary>Gets a user.</summary>
		public void Get(string name) {
			getUser(name);
			zapPassword();
		}
		
		/// <summary>Tests if a user exists.</summary>
		public bool Is(string name) {
			return isUser(name);
		}
		
		/// <overloads>Adds a user.</overloads>
		/// <summary>Adds basic user info</summary>
		public void Add(string name, string firstname, string surname, string email, string telno, string secret, string password, string group) {
			Add(name, firstname, surname, email, telno, secret, password, group, "", "", "", "");
		}
		/// <summary>Adds basic + extended user info</summary>
		public void Add(string name, string firstname, string surname, string email, string telno, string secret, string password, string group, string field1, string field2, string field3, string field4) {
			if (_users.SelectSingleNode("//user[@name='"+name+"']") != null)
				throw(new x_exception("error_exists", String.Concat("user with this name (", name, ") exists")));
			XmlElement userel = _users.CreateElement("user");
			XmlElement lastel = (XmlElement) _users.SelectSingleNode("//users").LastChild;
			int lastid = (lastel == null)? 0 : Convert.ToInt32(lastel.GetAttribute("id"))+1;
			userel.SetAttribute("id", lastid.ToString());
			userel.SetAttribute("name", name);
			userel.SetAttribute("password", password);
			userel.SetAttribute("group", group);
			userel.InnerXml = _root.SelectSingleNode("//default/blogspace/users/user").InnerXml;
			userel.SelectSingleNode("firstname").InnerText = firstname;
			userel.SelectSingleNode("surname").InnerText = surname;
			userel.SelectSingleNode("email").InnerText = email;
			userel.SelectSingleNode("telno").InnerText = telno;
			userel.SetAttribute("secret", secret);
			addField(userel, "field1", field1);
			addField(userel, "field2", field2);
			addField(userel, "field3", field3);
			addField(userel, "field4", field4);
			Add(userel);
		}
		/// <summary>Adds user info in xml element</summary>
		public void Add(XmlElement userel) {
			_users.SelectSingleNode("//users").AppendChild(userel);
			_users.Save(path());
			user = (XmlElement) userel.CloneNode(true);
			zapPassword();
		}
		private void addField(XmlElement el, string fieldName, string fieldValue) {
			if (fieldValue != "" && fieldValue != null) {
				XmlElement fieldEl = _users.CreateElement(fieldName);
				fieldEl.InnerText = fieldValue;
				el.AppendChild(fieldEl);
			}
		}
		
		/// <summary>Delete User.</summary>
		public void Delete(string name) {
			XmlElement userel = get(name);
			userel.ParentNode.RemoveChild(userel);
			_users.Save(path());
		}
		
		/// <overloads>Update existing user.</overloads>
		/// <summary>Update basic user info</summary>
		public void Update(string name, string firstname, string surname, string email, string telno) {
			update(name, firstname, surname, email, telno, "", "", "", "", "", "");
		}
		/// <summary>Update basic user info + profile</summary>
		public void Update(string name, string firstname, string surname, string email, string telno, string group) {
			update(name, firstname, surname, email, telno, group, "", "", "", "", "");
		}
		/// <summary>Update basic user info + profile and password</summary>
		public void Update(string name, string firstname, string surname, string email, string telno, string group, string password) {
			update(name, firstname, surname, email, telno, group, password, "", "", "", "");
		}
		/// <summary>Update basic + extended user info</summary>
		public void Update(string name, string firstname, string surname, string email, string telno, string group, string password, string field1, string field2, string field3, string field4) {
			update(name, firstname, surname, email, telno, group, password, field1, field2, field3, field4);
		}
		/// <summary>update existing user.</summary>
		private void update(string name, string firstname, string surname, string email, string telno, string group, string password, string field1, string field2, string field3, string field4) {
			XmlElement userel = get(name);
			if (group != "")
				userel.SetAttribute("group", group);
			if (password != "")
				userel.SetAttribute("password", password);
			userel.SelectSingleNode("firstname").InnerText = firstname;
			userel.SelectSingleNode("surname").InnerText = surname;
			userel.SelectSingleNode("email").InnerText = email;
			userel.SelectSingleNode("telno").InnerText = telno;
			updateField(userel, "field1", field1);
			updateField(userel, "field2", field2);
			updateField(userel, "field3", field3);
			updateField(userel, "field4", field4);
			_users.Save(path());
			user = (XmlElement) userel.CloneNode(true);
			zapPassword();
		}
		private void updateField(XmlElement el, string fieldName, string fieldValue) {
			if (fieldValue != "" && fieldValue != null) {
				XmlElement fieldEl = (el.SelectSingleNode(fieldName) != null)? (XmlElement)el.SelectSingleNode(fieldName)  : _users.CreateElement(fieldName);
				fieldEl.InnerText = fieldValue;
				el.AppendChild(fieldEl);
			}
		}
		
		
		/// <summary>
		/// ChangeLogin:: change the user's login details.
		/// </summary>
		public void ChangeLogin(string name, string newpass, string secret) {
			XmlElement userel = get(name);
			userel.SetAttribute("password", newpass);
			userel.SetAttribute("secret", secret);
			_users.Save(path());
		}
		
		/// <summary>
		/// ChangePassword:: change the user's password.
		/// </summary>
		public void ChangePassword(string name, string oldpass, string newpass) {
			XmlElement userel = get(name);
			if (userel.GetAttribute("password") != oldpass)
				throw(new x_exception("error_password", "incorrect password"));
			else
				userel.SetAttribute("password", newpass);
			_users.Save(path());
		}
		
		/// <overloads>List all users.</overloads>
		/// <summary>All</summary>
		public XmlNode List() {
			XmlNode users = _users.SelectSingleNode("//users").CloneNode(true);
			zapPasswords(users, true);
			return(users);
		}
		/// <summary>All within a profile</summary>
		public XmlNode List(string group) {
			XmlNode users = _users.SelectSingleNode("//users").CloneNode(true);
			foreach (XmlNode user in users.SelectNodes(String.Concat("user[@group!='", group, "']"))) {
				users.RemoveChild(user);
			}
			zapPasswords(users, true);
			return(users);
		}
		
		/// <summary>Add a blogspace to a user.</summary>
		public void AddBlogspace(string name, string user, string group, bool isdefault) {
			XmlElement userel = get(user);
			XmlElement spaces = (XmlElement) userel.SelectSingleNode("blogspaces");
			XmlElement space = (XmlElement) userel.SelectSingleNode("blogspaces/blogspace[@name='"+name+"']");
			if (space == null) {
				space = _users.CreateElement("blogspace");
				space.SetAttribute("name", name);
				spaces.AppendChild(space);
			}
			// set (or remove) group attribute
			if (group != "") {
				string usergroup = userel.GetAttribute("group");
				if (usergroup != group)
					space.SetAttribute("group", group);
				else
					space.RemoveAttribute("group");
			}
			// set (or remove) default attribute
			if (isdefault)
				spaces.SetAttribute("default", name);
			else if (spaces.GetAttribute("default") == name)
				spaces.RemoveAttribute("default");
			_users.Save(path());
			getUser(user);
			zapPassword();
		}
		
		/// <summary>Remove a blogspace from all users.</summary>
		public void RemoveBlogspace(string name) {
			foreach (XmlNode user in _users.SelectNodes("//users/user")) {
				XmlElement spaces = (XmlElement) user.SelectSingleNode("blogspaces");
				XmlElement space = (XmlElement) user.SelectSingleNode("blogspaces/blogspace[@name='"+name+"']");
				if (space != null) {
					spaces.RemoveChild(space);
					if (spaces.GetAttribute("default") == name)
						spaces.RemoveAttribute("default");
				}
			}
			_users.Save(path());
		}
		/// <summary>Remove a blogspace from a user.</summary>
		public void RemoveBlogspace(string name, string user) {
			XmlElement userel = get(user);
			XmlElement spaces = (XmlElement) userel.SelectSingleNode("blogspaces");
			XmlElement space = (XmlElement) userel.SelectSingleNode("blogspaces/blogspace[@name='"+name+"']");
			if (space != null) {
				spaces.RemoveChild(space);
				if (spaces.GetAttribute("default") == name)
					spaces.RemoveAttribute("default");
				_users.Save(path());
			}
			getUser(user);
			zapPassword();
		}
		
		/// <summary>Set a blogspace to be default.</summary>
		public void DefaultBlogspace(string name, string user) {
			XmlElement userel = get(user);
			XmlElement spaces = (XmlElement) userel.SelectSingleNode("blogspaces");
			XmlElement space = (XmlElement) userel.SelectSingleNode("blogspaces/blogspace[@name='"+name+"']");
			if (space != null) {
				spaces.SetAttribute("default", name);
				_users.Save(path());
			}
			getUser(user);
			zapPassword();
		}
		
		/// <summary>Get user element as a string of xml</summary>
		public string UserXml() {
			return(user.OuterXml);
		}
		
		/// <summary>Adds the user description blog file (ie username.xml in User directory).</summary>
		public void AddBlog(string name, string author, string title, string html) {
			x_blog blogx = new x_blog();
			blogx.Add(path(name), author, title, html);
			blogx.Get(1);
			user = blogx.Blog;
		}
		
		/// <summary>Gets the user description blog file (ie username.xml in User directory).</summary>
		public void GetBlog(string name) {
			x_blog blogx = new x_blog(path(name));
			blogx.Get(1);
			user = blogx.Blog;
		}
		
		/// <overloads>SendEmail: Sends an email to a group of users.</overloads>
		/// <summary>Sends an email to a group of users.</summary>
		public XmlNode SendEmail(string group, string subject, string message, string filename) {
			using (MailMessage emailmsg = new MailMessage()) {
				x_config config = new x_config();
				emailmsg.From = new MailAddress(config.EmailFrom);
				emailmsg.To.Add(new MailAddress(config.EmailTo));
				emailmsg.Subject = subject;
				emailmsg.Body = message;
				XmlNode users = List(group);
				foreach (XmlNode user in users.SelectNodes("user")) {
					emailmsg.Bcc.Add(new MailAddress(user.SelectSingleNode("email").InnerText));
				}
				emailmsg.BodyEncoding = Encoding.ASCII;
				emailmsg.IsBodyHtml = true;
				emailmsg.Priority = MailPriority.Normal;
				emailmsg.Attachments.Add(new Attachment(filename));
				
				SmtpClient smtp = new SmtpClient();
				string server = config.EmailHost;
				if (server != "localhost")
					smtp.Host = server;
				smtp.Send(emailmsg);
				return(users);
			}
		}
		
		/// <summary>Sends an email of a blog to a group of users within a blogspace.</summary>
		public XmlNode SendEmail(string group, string subject, string name, string topic, string filepath, string logpath, x_blog blogx, x_blogspace blogspacex) {
			x_config config = new x_config();
			SmtpClient smtp = new SmtpClient();
			string server = config.EmailHost;
			if (server != "localhost")
				smtp.Host = server;
			// Create message
			using (MailMessage emailmsg = new MailMessage()) {
				emailmsg.BodyEncoding = Encoding.ASCII;
				emailmsg.IsBodyHtml = true;
				emailmsg.Priority = MailPriority.Normal;
				emailmsg.Subject = subject;
				emailmsg.From = new MailAddress(config.EmailFrom);
				emailmsg.Bcc.Add(new MailAddress(config.EmailBcc));
				// Create message body...
				// ...load xsl
				XslCompiledTransform xslt = new XslCompiledTransform();
				XsltArgumentList xsltArgs;
				xsltArgs = new XsltArgumentList();
				xsltArgs.AddParam("name", "", name);
				xsltArgs.AddParam("topic", "", topic);
				xslt.Load(filepath);
				// ...load blog
				decodeContent(blogx.Blog);
				XmlDocument blogdoc = new XmlDocument();
				blogdoc.LoadXml(String.Concat("<news topic='", topic, "'>", blogspacex.Blogspace.OuterXml, blogx.Blog.OuterXml, "<users/></news>"));
				// ...transform blog to html string
				StringWriter writer = new StringWriter();
				xslt.Transform(blogdoc, xsltArgs, writer);
				
				emailmsg.Body = writer.ToString();
				
				XmlNode users = List(group);
				XmlNode usersnode = blogdoc.SelectSingleNode("//users");
				foreach (XmlNode user in users.SelectNodes(String.Concat("user[blogspaces/blogspace/@name='", name, "']"))) {
					XmlElement userel = (XmlElement) usersnode.AppendChild(blogdoc.CreateElement("user"));
					userel.SetAttribute("email", user.SelectSingleNode("email").InnerText);
					userel.SetAttribute("senddate", DateTime.Now.ToString("s", DateTimeFormatInfo.InvariantInfo));
					try {
						emailmsg.To.Add(new MailAddress(user.SelectSingleNode("email").InnerText));
						smtp.Send(emailmsg);
						userel.SetAttribute("sendstatus", "OK");
					} catch (System.Exception e) {
						userel.SetAttribute("sendstatus", "NotOK");
						XmlElement senderror = (XmlElement)userel.AppendChild(userel.OwnerDocument.CreateElement("senderror"));
						senderror.InnerText = e.Message;
						XmlElement sendtrace = (XmlElement)userel.AppendChild(userel.OwnerDocument.CreateElement("sendtrace"));
						sendtrace.InnerText = e.StackTrace;
					}
				}
				blogdoc.Save(String.Concat(logpath, name, "/", DateTime.Now.ToString("yyyyMMdd"), ".xml"));
				StreamWriter sr = File.CreateText(String.Concat(logpath, name, "/", DateTime.Now.ToString("yyyyMMdd"), ".html"));
				sr.Write(writer.ToString());
				sr.Close();
				return(blogdoc.DocumentElement);
			}
		}
		#endregion

		#region Protected methods
		#endregion

		#region Private methods
		/// <summary>checkToken::checks and resets a valid token</summary>
		private void checkToken() {
			if (user == null)
				throw(new x_exception("error_valid", "token not valid"));
			// Now check token still valid...
			DateTime expired = DateTime.Parse(user.GetAttribute("expiry"), DateTimeFormatInfo.InvariantInfo);
			DateTime current = DateTime.Now;
			TimeSpan diff = current.Subtract(expired);
			if (diff.TotalMinutes > _expiry )
				throw(new x_exception("error_expired", String.Concat("token expired (by ", diff.TotalMinutes.ToString(), " minutes)")));
			else
				resetToken();
		}
		/// <summary>resetToken::resets user token expiry</summary>
		private void resetToken() {
			XmlElement userel = (XmlElement) _users.SelectSingleNode("//user[@name='"+user.GetAttribute("name")+"']");
			userel.SetAttribute("expiry", DateTime.Now.AddMinutes(_expiry).ToString("s"));
			user = (XmlElement) userel.CloneNode(true);
		}
		/// <summary>checkPublic::checks user is same as token owner</summary>
		private void checkUser(string token, string user) {
			XmlElement userel = (XmlElement) _users.SelectSingleNode("//users/user[@token='"+token+"']");
			if (userel.GetAttribute("name") != user)
				throw(new x_exception("error_usertoken", "user not owner of token"));
		}
		/// <summary>checkPublic::checks access is 'public'</summary>
		private bool checkPublic(string right) {
			XmlNode rights = _root.SelectSingleNode("//groups/group[@name='public']/rights/"+right);
			return (rights != null);
		}
		/// <summary>checkRight::checks access right</summary>
		private void checkRight(string right) {
			XmlNode rights = _root.SelectSingleNode("//groups/group[@name='"+user.GetAttribute("group")+"']/rights");
			if (rights == null)
				throw(new x_exception("error_group", "unknown rights group"));
			else if (rights.SelectSingleNode(right) == null)
				throw (new x_exception("error_right", String.Concat("access denied for: '", right, "' for group '", user.GetAttribute("group"), "'")));
		}
		/// <summary>getToken::generates a token</summary>
		/// <note>NB: this method is a potential inclusion in a x_blogbase class</note>
		private string getToken() {
			Random rdm = new Random(unchecked((int)DateTime.Now.Ticks)); 
			Byte[] b = new Byte[16];
			rdm.NextBytes(b);
			Guid thisguid = new Guid(b) ;
			return(thisguid.ToString("D"));
		}
		/// <summary>setToken::sets a user token and expiry</summary>
		private void setToken() {
			token = getToken();
			XmlElement userel = (XmlElement) _users.SelectSingleNode("//user[@name='"+user.GetAttribute("name")+"']");
			userel.SetAttribute("token", token);
			userel.SetAttribute("expiry", DateTime.Now.AddMinutes(_expiry).ToString("s"));
			user = (XmlElement) userel.CloneNode(true);
		}
		/// <summary>zapPassword::Replaces user password characters with "*"'s.</summary>
		/// <note>NB: this method is a potential inclusion in a x_blogbase class</note>
		private void zapPassword() {
			Regex reg = new Regex("[a-zA-Z0-9]");
			string stars = reg.Replace(user.GetAttribute("password"), "*");
			user.SetAttribute("password", stars);
		}
		/// <summary>zapPasswords::Replaces all users passwords characters with "*"'s.</summary>
		private void zapPasswords(XmlNode which, bool all) {
			XmlNodeList users = which.SelectNodes("//user");
			XmlElement userel;
			Regex reg = new Regex("[a-zA-Z0-9]");
			string stars;
			foreach (XmlNode user in users){
        		userel = (XmlElement) user;
				if (all) {
					userel.RemoveAttribute("token");
					userel.RemoveAttribute("expiry");
				}
				stars = reg.Replace(userel.GetAttribute("password"), "*");
				userel.SetAttribute("password", stars);
			}
		}
		private XmlElement get(string name) {
			scan(name);
			XmlElement userel = (XmlElement) _users.SelectSingleNode("//user[@name='"+name+"']");
			if (userel == null)
				throw(new x_exception("error_notexist", "user with this name ("+name+") does not exist"));
			return(userel);
		}
		private void getUser(string name) {
			user = (XmlElement) get(name).CloneNode(true);
		}
		private bool isUser(string name) {
			scan(name);
			return (_users.SelectSingleNode("//user[@name='"+name+"']") != null);
		}
		/// <summary>path:: returns the relevant file path.</summary>
		private string path(string name) { return String.Concat(_path, "Users/", name, ".xml"); }
		private string path() { return String.Concat(_path, "Users/blogx.xml"); }
		/// <summary>
		/// Decodes the content within blog xml (similar to the displayX  base class)
		/// </summary>
		/// <note>NB: this method is a potential inclusion in a x_blogbase class</note>
		private void decodeContent(XmlNode _blogs) {
			// the following is for backward compatability
			foreach (XmlNode textnode in _blogs.SelectNodes("//blog/text")){
				XmlElement textel = (XmlElement) textnode;
				textel.SetAttribute("decoded", "yes");
				textel.InnerXml = (textel.GetAttribute("type") == "html" || textel.GetAttribute("html") == "yes")? HttpUtility.HtmlDecode(textel.InnerText) : HttpUtility.HtmlDecode(textel.InnerXml);
			}
			foreach (XmlNode textnode in _blogs.SelectNodes("//blog/bloghtml")){
				XmlElement textel = (XmlElement) textnode;
				textel.SetAttribute("decoded", "yes");
				textel.InnerXml = HttpUtility.HtmlDecode(textel.InnerText);
			}
			foreach (XmlNode textnode in _blogs.SelectNodes("//blog/blogtext")){
				XmlElement textel = (XmlElement) textnode;
				textel.SetAttribute("decoded", "yes");
				textel.InnerXml = HttpUtility.HtmlDecode(textel.InnerXml);
			}
		}
		/// <summary>scan for xpath injection attack</summary>
		private void scan(string input) {
			if (input.IndexOf("'") >= 0)
				throw(new x_exception("error_injection", "potential xpath injection attack detected"));
		}
		#endregion
	}
}
